﻿using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;

#pragma warning disable CS8632 // The annotation for nullable reference types should only be used in code within a '#nullable' annotations context.
#pragma warning disable CS8603 // Possible null reference return.

namespace Hsenl {
    // HTask只是一个空壳子, 具体实现是由其内部的body来实现
    // 这么做的意义在于不让用户直接接触到body, 以防止用户保存body的引用, 从而造成未知的风险
    [AsyncMethodBuilder(typeof(AsyncHTaskMethodBuilder))]
    [StructLayout(LayoutKind.Auto)]
    public partial struct HTask : IHTask {
        public const int MaxVersion = int.MaxValue - 2;
        public const int MinVersion = int.MinValue;

        private readonly HTaskType _taskType;
        private volatile IHTaskBody? _body;
        private readonly int _version;

        internal IHTaskBody Body => this._body;

        [DebuggerHidden]
        internal HTask(IHTaskBody? body, HTaskType taskType) {
            this._taskType = taskType;
            this._body = body;
            this._version = body?.Version ?? 0;
        }

        [DebuggerHidden]
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public Awaiter GetAwaiter() {
            return new Awaiter(this);
        }

        public bool IsCompleted {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get {
                if (this.IsNull)
                    return true;

                return this._body!.IsCompleted;
            }
        }

        internal void SetResult_Internal() {
            var t = this._body;
            if (t != null) {
                if (this._version != t.Version) {
                    throw new Exception("HTask's body is expired!");
                }

                t.SetResult();
            }
        }

        [DebuggerHidden]
        public void SetResult() {
            if (this.IsCompleted)
                return;
            
            if (this._taskType == HTaskType.Builder)
                // 之所以要加这个限制, 因为假如我拿到系统构建的 task, 然后 SetResult或者SetException, 等到系统再执行这些操作的时候, 会出很多问题.
                // task 只有两种创建方法, 一种是我们通过 HTask.Create, 另一种就是作为返回值, 由系统自行创建.
                throw new InvalidOperationException("HTask's type is builder, can't set result!");

            this.SetResult_Internal();
        }

        internal void SetException_Internal(Exception e) {
            var t = Interlocked.Exchange(ref this._body, null);
            if (t != null) {
                if (this._version != t.Version) {
                    throw new Exception("HTask's body is expired!");
                }

                t.SetException(e);
            }
        }

        [DebuggerHidden]
        public void SetException(Exception e) {
            if (this.IsCompleted)
                return;
            
            if (this._taskType == HTaskType.Builder)
                throw new InvalidOperationException("HTask's type is builder, can't set exception!!");

            this.SetException_Internal(e);
        }

        // 一个类型想要能被await, 必须有GetAwaiter()函数, 拓展的也行, 并返回一个ICriticalNotifyCompletion
        // 一个类型想要能被async, 必须指定一个AsyncMethodBuilder
        public readonly struct Awaiter : ICriticalNotifyCompletion {
            private readonly HTask _task;

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public Awaiter(HTask task) {
                this._task = task;
            }

            public bool IsCompleted {
                [MethodImpl(MethodImplOptions.AggressiveInlining)]
                get => this._task._body?.IsCompleted ?? true;
            }

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public void GetResult() {
                this._task._body?.GetResult();
            }

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public void OnCompleted(Action continuation) {
                this._task._body!.OnCompleted(continuation);
            }

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public void UnsafeOnCompleted(Action continuation) {
                this._task._body!.UnsafeOnCompleted(continuation);
            }
        }
    }


    [AsyncMethodBuilder(typeof(AsyncHTaskMethodBuilder<>))]
    [StructLayout(LayoutKind.Auto)]
    public partial struct HTask<T> : IHTask {
        private readonly HTaskType _taskType;
        private volatile IHTaskBody<T>? _body;
        private readonly int _version;

        [DebuggerHidden]
        internal IHTaskBody<T> Body => this._body;

        [DebuggerHidden]
        internal HTask(IHTaskBody<T>? body, HTaskType taskType) {
            this._taskType = taskType;
            this._body = body;
            this._version = body?.Version ?? 0;
        }

        [DebuggerHidden]
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public Awaiter GetAwaiter() {
            return new Awaiter(this);
        }

        public bool IsCompleted {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get {
                if (this.IsNull)
                    return true;

                return this._body!.IsCompleted;
            }
        }

        public void SetResult_Internal(T value) {
            var t = this._body;
            if (t != null) {
                if (this._version != t.Version) {
                    throw new Exception("HTask's body is expired!");
                }

                t.SetResult(value);
            }
        }

        [DebuggerHidden]
        public void SetResult(T value) {
            if (this.IsCompleted)
                return;
            
            if (this._taskType == HTaskType.Builder)
                throw new InvalidOperationException("HTask's type is builder, can't set result!");

            this.SetResult_Internal(value);
        }

        internal void SetException_Internal(Exception e) {
            var t = this._body;
            if (t != null) {
                if (this._version != t.Version) {
                    throw new Exception("HTask's body is expired!");
                }

                t.SetException(e);
            }
        }

        [DebuggerHidden]
        public void SetException(Exception e) {
            if (this.IsCompleted)
                return;
            
            if (this._taskType == HTaskType.Builder)
                throw new InvalidOperationException("HTask's type is builder, can't set exception!!");

            this.SetException_Internal(e);
        }

        public readonly struct Awaiter : ICriticalNotifyCompletion {
            private readonly HTask<T> _task;

            [DebuggerHidden]
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public Awaiter(HTask<T> task) {
                this._task = task;
            }

            public bool IsCompleted {
                [MethodImpl(MethodImplOptions.AggressiveInlining)]
                get => this._task._body?.IsCompleted ?? true;
            }

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public T GetResult() {
                var t = this._task._body;
                if (t == null)
                    return default;

                return t.GetResult();
            }

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public void OnCompleted(Action continuation) {
                this._task._body!.OnCompleted(continuation);
            }

            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            public void UnsafeOnCompleted(Action continuation) {
                this._task._body!.UnsafeOnCompleted(continuation);
            }
        }
    }
}